﻿using System;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Modules.HighLevel
{
    [ModuleDisplay("Flanger")]
    class FlangerModule : BaseModule
    {
        private InputModuleParameter mDelayTime ;
        private InputModuleParameter mFeedback;
        private InputModuleParameter mOutput;

        private InputModuleParameter mBufferLength;
        private int mCounter;
        private double[] mBuffer;

        public ModuleInput Input;
        public ModuleOutput Output;

        public FlangerModule(ModuleManager manager)
            : base(manager)
        {
            int buffer = 1024;

            mCounter = 0;
            mBuffer = new double[1024];

            AddInput(Input = new ModuleInput("Input", IOType.Audio, this));
            AddOutput(Output = new ModuleOutput("Output", IOType.Audio, this));

            // Flanging.
            mDelayTime = new InputModuleParameter(this, "Fla", 0, IOType.Audio);
            mDelayTime.Label = "Flanging";
            mDelayTime.MinValue = 0.001;
            mDelayTime.MaxValue = 1.0;
            AddModuleParameter(mDelayTime);

            // Buffer.
            mBufferLength = new InputModuleParameter(this, "Fla", buffer, IOType.Audio);
            mBufferLength.Label = "Buffer";
            mBufferLength.MinValue = 1;
            mBufferLength.MaxValue = 1024;
            mBufferLength.DirectSet += delegate (double value)
            {
                SetBufferLength((int)value);
            };
            AddModuleParameter(mBufferLength);

            // Feedback.
            mFeedback = new InputModuleParameter(this, "Fla", 0, IOType.Audio);
            mFeedback.Label = "Feedback";
            mFeedback.MinValue = 0.001;
            mFeedback.MaxValue = 0.9;
            AddModuleParameter(mFeedback);

            // Output.
            mOutput = new InputModuleParameter(this, "Fla", 0, IOType.Audio);
            mOutput.Label = "Output";
            mOutput.MinValue = 0;
            mOutput.MaxValue = 1;
            AddModuleParameter(mOutput);
        }

        public void SetBufferLength(int length)
        {
            if (length < 64)
                length = 64;
            
            Array.Clear(mBuffer, length, 1024 - length);
        }

        public void Clear()
        {
            Array.Clear(mBuffer, 0, mBuffer.Length);
        }

        public void Reset()
        {
            Clear();
        }

        public override void Process()
        {
            if (Input.IsConnected)
            {
                double[] bufferIn = Input.FromOutput.AudioBuffer.Buffer;
                double[] bufferOut = Output.AudioBuffer.Buffer;
                int length = bufferIn.Length;
                for (int i = 0; i < length; ++i)
                {
                    double back = mCounter - mDelayTime[i];
                    if (back < 0.0)
                        back = mBufferLength[i] + back;

                    int index0 = (int)Math.Floor(back);
                    int index_1 = index0 - 1;
                    int index1 = index0 + 1;
                    int index2 = index0 + 2;

                    int bufferLength = (int)mBufferLength[i];

                    if (index_1 < 0) index_1 = bufferLength - 1;
                    if (index_1 < 0) index_1 = 0;
                    if (index1 >= bufferLength) index1 = 0;
                    if (index2 >= bufferLength) index2 = 0;

                    double y_1 = mBuffer[index_1];
                    double y0 = mBuffer[index0];
                    double y1 = mBuffer[index1];
                    double y2 = mBuffer[index2];

                    double x = back - index0;

                    double c0 = y0;
                    double c1 = 0.5f * (y1 - y_1);
                    double c2 = y_1 - 2.5f * y0 + 2.0f * y1 - 0.5f * y2;
                    double c3 = 0.5f * (y2 - y_1) + 1.5f * (y0 - y1);

                    double outputSample = ((c3 * x + c2) * x + c1) * x + c0;

                    mBuffer[mCounter] = bufferIn[i] + outputSample * mFeedback[i];
                    ++mCounter;

                    if (mCounter >= bufferLength)
                        mCounter = 0;

                    bufferOut[i] = outputSample * mOutput[i];
                }
            }
        }
    }
}
